package edu.ubb.bibliospring.backend.repository.bean;

import edu.ubb.bibliospring.backend.exception.BiblioSpringRepositoryException;
import edu.ubb.bibliospring.backend.model.AbstractModel;
import edu.ubb.bibliospring.backend.repository.BaseRepository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;

public abstract class BaseRepositoryBean<T extends AbstractModel, I> implements BaseRepository<T, I> {

    private final Class<T> clazz;

    @PersistenceContext(unitName = "bsUnit")
    private EntityManager entityManager;

    public BaseRepositoryBean(final Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public List<T> getAll() throws BiblioSpringRepositoryException {
        try {
            final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            final CriteriaQuery<T> criteriaQuery = cb.createQuery(clazz);
            final Root<T> rootEntry = criteriaQuery.from(clazz);
            final CriteriaQuery<T> all = criteriaQuery.select(rootEntry);
            final TypedQuery<T> allQuery = entityManager.createQuery(all);
            return allQuery.getResultList();
        } catch (IllegalArgumentException | PersistenceException e) {
            throw new BiblioSpringRepositoryException("Error in method getAll.", e);
        }
    }

    @Override
    public T getById(final I id) throws BiblioSpringRepositoryException {
        try {
            return entityManager.find(clazz, id);
        } catch (final PersistenceException pe) {

            throw new BiblioSpringRepositoryException("BaseRepositoryBean getById method!", pe);
        }
    }

    @Override
    public T save(final T item) throws BiblioSpringRepositoryException {
        try {
            entityManager.persist(item);
            entityManager.flush();
            return item;
        } catch (final PersistenceException pe) {
            throw new BiblioSpringRepositoryException("BaseRepositoryBean save method!", pe);
        }
    }

    @Override
    public T merge(T item) throws BiblioSpringRepositoryException {
        try {
            item = entityManager.merge(item);
            return item;
        } catch (final PersistenceException pe) {
            throw new BiblioSpringRepositoryException("BaseRepositoryBean merge method!", pe);
        }
    }

    @Override
    public void delete(final T item) throws BiblioSpringRepositoryException {
        try {
            final T merged = entityManager.merge(item);
            entityManager.remove(merged);
        } catch (final PersistenceException pe) {
            throw new BiblioSpringRepositoryException("BaseRepositoryBean delete method!", pe);
        }
    }
}
